package com.kaixinff.net;
import java.io.IOException;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

public class MutiCookieHandler {
	
	private Map<String, String> cache = new LinkedHashMap<String, String>();
	
	public MutiCookieHandler() {
		
	}
	
	public MutiCookieHandler(Collection<String> cookies) {
		setCookies(cookies);
	}
	
	void setCookies(Collection<String> cookies) {
		for (String cookie : cookies) {
			List<HttpCookie> httpCookies = HttpCookie.parse(cookie);
			if (httpCookies.size() > 0) {
				HttpCookie httpCookie = httpCookies.get(0);
				cache.put(httpCookie.getName(), cookie);
			}
		}
	}
	
	Collection<String> getCookies() {
		return cache.values();
	}
	
	String getCookie(String key) {
		return cache.get(key);
	}
	
	/**
	 * Gets all the applicable cookies from a cookie cache for the specified uri
	 * in the request header.
	 * 
	 * HTTP protocol implementers should make sure that this method is called
	 * after all request headers related to choosing cookies are added, and
	 * before the request is sent.
	 * 
	 * @param uri
	 *            a <code>URI</code> to send cookies to in a request
	 * @param requestHeaders
	 *            - a Map from request header field names to lists of field
	 *            values representing the current request headers
	 * @return an immutable map from state management headers, with field names
	 *         "Cookie" or "Cookie2" to a list of cookies containing state
	 *         information
	 * 
	 * @throws IOException
	 *             if an I/O error occurs
	 * @throws IllegalArgumentException
	 *             if either argument is null
	 * @see #put(URI, Map)
	 */
	public void get(HttpURLConnection conn) throws IOException {
		StringBuilder sb = new StringBuilder();
		for (String item : cache.values()) {
			List<HttpCookie> cookies = HttpCookie.parse(item);
			if (cookies.size() > 0) {
				HttpCookie cookie = cookies.get(0);
				if (cookie.hasExpired()) {
					
				} else if (domainMatches(cookie.getDomain(),HttpClient.getHost(conn.getURL().toString()))){
					if (sb.length() > 0) {
						sb.append("; ");
					}
					sb.append(cookie.getName() + "=" + cookie.getValue());
				}
			}
		}
		if (sb.length() > 0) {
			conn.addRequestProperty("Cookie", sb.toString());
		}
	}

	/**
	 * Sets all the applicable cookies, examples are response header fields that
	 * are named Set-Cookie2, present in the response headers into a cookie
	 * cache.
	 * 
	 * @param uri
	 *            a <code>URI</code> where the cookies come from
	 * @param responseHeaders
	 *            an immutable map from field names to lists of field values
	 *            representing the response header fields returned
	 * @throws IOException
	 *             if an I/O error occurs
	 * @throws IllegalArgumentException
	 *             if either argument is null
	 * @see #get(URI, Map)
	 */
	public void put(HttpURLConnection conn)
			throws IOException {
		List<String> setCookieList = new ArrayList<String>();
		for (int i = 0; ; i++) {
			String key = conn.getHeaderFieldKey(i);
			String value = conn.getHeaderField(i);
			if (value == null) {
				break;
			}
			if ("Set-Cookie".equalsIgnoreCase(key)) {
				setCookieList.add(value);
			}
		}
		for (int i = setCookieList.size() - 1; i >= 0; i --) {
			String setCookie = setCookieList.get(i);
			String setCookieLower = setCookie.toLowerCase();
        	setCookie = setCookie.replace("; httponly", "");
        	if (setCookieLower.indexOf("max-age=") == -1) {
				setCookie += "; max-age=-1";
			}
        	int beginIndex = setCookieLower.indexOf("expires=");
			if (beginIndex != -1) {
				int endIndex = setCookie.indexOf(";", beginIndex);
				if (endIndex == -1) {
					endIndex = setCookie.length();
				}
				String dateStr = setCookieLower.substring(beginIndex + 8, endIndex);
				SimpleDateFormat df = new SimpleDateFormat("EEE',' dd-MMM-yyyy HH:mm:ss 'gmt'", Locale.US);
		        df.setTimeZone(TimeZone.getTimeZone("GMT"));
		        try {
					Date date = df.parse(dateStr);
					SimpleDateFormat df2 = new SimpleDateFormat("EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'");
					setCookie = setCookie.substring(0, beginIndex + 8) + df2.format(date) + setCookie.substring(endIndex);
				} catch (ParseException e) {
					e.printStackTrace();
				}
	        }
			setCookieList.set(i, setCookie);
		}
		for (String item : setCookieList) {
			if (item == null) {
				continue;
			}
			List<HttpCookie> cookies = HttpCookie.parse(item);
			if (cookies.size() > 0) {
				HttpCookie cookie = cookies.get(0);
				cache.put(cookie.getName(), item);
			}
		}
	}
	
	private boolean domainMatches(String domain, String host) {

		if (domain == null || host == null)
			return true;

		// if there's no embedded dot in domain and domain is not .local
		boolean isLocalDomain = ".local".equalsIgnoreCase(domain);
		int embeddedDotInDomain = domain.indexOf('.');
		if (embeddedDotInDomain == 0)
			embeddedDotInDomain = domain.indexOf('.', 1);
		if (!isLocalDomain
				&& (embeddedDotInDomain == -1 || embeddedDotInDomain == domain
						.length() - 1))
			return false;

		// if the host name contains no dot and the domain name is .local
		int firstDotInHost = host.indexOf('.');
		if (firstDotInHost == -1 && isLocalDomain)
			return true;

		int domainLength = domain.length();
		int lengthDiff = host.length() - domainLength;
		if (lengthDiff == 0) {
			// if the host name and the domain name are just string-compare
			// euqal
			return host.equalsIgnoreCase(domain);
		} else if (lengthDiff > 0) {
			// need to check H & D component
//			String H = host.substring(0, lengthDiff);
			String D = host.substring(lengthDiff);

			return D.equalsIgnoreCase(domain);
		} else if (lengthDiff == -1) {
			// if domain is actually .host
			return (domain.charAt(0) == '.' && host.equalsIgnoreCase(domain
					.substring(1)));
		}

		return false;
	}
}
